﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;

namespace System.Collections.Generic
{
    public class SynchronizedDictionary<TKey, TValue> : IDictionary<TKey, TValue>
    {
        private Dictionary<TKey, TValue> dicts;
        private ReaderWriterLockSlim locker;

        /// <summary>
        /// Атомарная операция извлечения с удалением значения по ключу
        /// </summary>
        /// <param name="key">Ключ</param>
        /// <param name="value">Полученное значение</param>
        /// <returns>Результат получения значения</returns>
        public bool TryExtractValue(TKey key, out TValue value)
        {
            using (Locker.Write(locker))
            {
                if (dicts.TryGetValue(key, out value))
                {
                    dicts.Remove(key);
                    return true;
                }
                else
                    return false;
            }
        }

        public bool TryAdd(TKey key, Func<TValue> predic)
        {
            using (Locker.UpgradeableRead(locker))
            {
                if (dicts.ContainsKey(key))
                    return false;
                else
                    using (Locker.Write(locker))
                    {
                        dicts.Add(key, predic());
                        return true;
                    }
            }
        }

        #region Конструкторы
        /// <summary>
        /// Конструктор
        /// </summary>
        /// <param name="SyncRootRead">Объект синхронизации чтения</param>
        /// <param name="SyncRootWrite">Объект синхронизации записи</param>
        /// <param name="dict">Коллекция-источник</param>
        public SynchronizedDictionary(ReaderWriterLockSlim locker, IDictionary<TKey, TValue> dict)
        {
            if (locker == null)
                this.locker = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion);
            this.dicts = new Dictionary<TKey, TValue>();

            if (dict != null)
                foreach (var item in dict)
                    this.dicts.Add(item.Key, item.Value);
        }

        /// <summary>
        /// Конструктор
        /// </summary>
        /// <param name="SyncRootRead">Объект синхронизации чтения</param>
        /// <param name="SyncRootWrite">Объект синхронизации записи</param>
        public SynchronizedDictionary(ReaderWriterLockSlim locker) :
            this(locker, null)
        {

        }

        /// <summary>
        /// Конструктор
        /// </summary>
        public SynchronizedDictionary() :
            this(null, null)
        { }

        /// <summary>
        /// Конструктор
        /// </summary>
        /// <param name="dict">Коллекция-источник</param>
        public SynchronizedDictionary(IDictionary<TKey, TValue> dict) :
            this(null, dict)
        {
        }
        #endregion

        #region IDictionary<Tkey,TValue> Members

        public void Add(TKey key, TValue value)
        {
            using (Locker.Write(locker))
                dicts.Add(key, value);
        }

        /// <summary>
        /// Атомарная операция добавления новой ассоциативной пары
        /// </summary>
        /// <param name="key"></param>
        /// <param name="value"></param>
        /// <returns></returns>
        public bool TryAdd(TKey key, TValue value)
        {
            using (Locker.UpgradeableRead(locker))
            {
                if (!dicts.ContainsKey(key))
                    using (Locker.Write(locker))
                    {
                        dicts.Add(key, value);
                        return true;
                    }
                else
                    return false;
            }
        }

        //public bool AddIfKey(TKey key, Func<TKey, bool> predicate, TValue Value)
        //{
        //    using (Locker.Write(locker))
        //    {
        //        if (predicate(key))
        //        {
        //            dicts.Add(key, Value);
        //            return true;
        //        }
        //        else
        //            return false;
        //    }
        //}

        public bool AddIfKey(TKey key, Func<TKey, bool> predicate, Func<TValue> FuncValue)
        {
            using (Locker.Write(locker))
            {
                if (predicate(key))
                {
                    dicts.Add(key, FuncValue());
                    return true;
                }
                else
                    return false;
            }
        }

        public bool ContainsKey(TKey key)
        {
            using (Locker.Read(locker))
                return dicts.ContainsKey(key);
        }

        public ICollection<TKey> Keys
        {
            get
            {
                return CollectionSyncWrapp<TKey>.Create(dicts.Keys, locker);
            }
        }

        public bool Remove(TKey key)
        {
            locker.EnterWriteLock();
            try
            { return dicts.Remove(key); }
            finally
            { locker.ExitWriteLock(); }
        }

        public bool TryGetValue(TKey key, out TValue value)
        {
            locker.EnterReadLock();
            try
            { return dicts.TryGetValue(key, out value); }
            finally
            { locker.ExitReadLock(); }
        }

        public ICollection<TValue> Values
        {
            get
            {
                return CollectionSyncWrapp<TValue>.Create(dicts.Values, locker);
            }
        }

        public TValue this[TKey key]
        {
            get
            {
                locker.EnterReadLock();
                try
                { return dicts[key]; }
                finally
                { locker.ExitReadLock(); }
            }
            set
            {
                locker.EnterWriteLock();
                try
                { dicts[key] = value; }
                finally
                { locker.ExitWriteLock(); }
            }
        }

        #endregion

        #region ICollection<KeyValuePair<Tkey,TValue>> Members

        public void Add(KeyValuePair<TKey, TValue> item)
        {
            locker.EnterWriteLock();
            try
            { dicts.Add(item.Key, item.Value); }
            finally
            { locker.ExitWriteLock(); }
        }

        public void Clear()
        {
            locker.EnterWriteLock();
            try
            { dicts.Clear(); }
            finally
            { locker.ExitWriteLock(); }
        }

        public bool Contains(KeyValuePair<TKey, TValue> item)
        {
            locker.EnterReadLock();
            try
            { return dicts.ContainsKey(item.Key) && dicts.ContainsValue(item.Value); }
            finally
            { locker.ExitReadLock(); }
        }

        public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex)
        {
            locker.EnterWriteLock();
            try
            {
                foreach (var item in dicts)
                    array[arrayIndex++] = item;
            }
            finally
            { locker.ExitWriteLock(); }
        }

        public int Count
        {
            get
            {
                locker.EnterReadLock();
                try
                { return dicts.Count; }
                finally
                { locker.ExitReadLock(); }
            }
        }

        private bool _IsReadOnly;
        public bool IsReadOnly
        {
            get
            {
                locker.EnterReadLock();
                try
                { return _IsReadOnly; }
                finally
                { locker.ExitReadLock(); }
            }
            protected set
            {
                locker.EnterWriteLock();
                try
                { _IsReadOnly = value; }
                finally
                { locker.ExitWriteLock(); }
            }
        }

        public bool Remove(KeyValuePair<TKey, TValue> item)
        {

            locker.EnterWriteLock();
            try
            { return dicts.Remove(item.Key); }
            finally
            { locker.ExitWriteLock(); }
        }

        #endregion

        #region IEnumerable<KeyValuePair<Tkey,TValue>> Members

        public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
        {
            return SynchronizedEnumerator<KeyValuePair<TKey, TValue>>.Create(GetInnerEnumerator, locker);
        }
        private IEnumerator<KeyValuePair<TKey, TValue>> GetInnerEnumerator()
        {
            return dicts.GetEnumerator();
        }
        #endregion

        #region IEnumerable Members

        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }

        #endregion
    }

    /// <summary>
    /// Потокобезопасная оболочка для коллекции
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public class CollectionSyncWrapp<T> : ICollection<T>
    {
        private ICollection<T> inner;
        private ReaderWriterLockSlim locker;
        #region Конструкторы
        protected CollectionSyncWrapp(ICollection<T> coll, ReaderWriterLockSlim Locker)
        {
            inner = coll;
            this.locker = (Locker != null) ?
                Locker :
                new ReaderWriterLockSlim();
        }
        public static CollectionSyncWrapp<T> Create(ICollection<T> coll, ReaderWriterLockSlim Locker)
        {
            CollectionSyncWrapp<T> res = null;
            if (coll != null)
                res = new CollectionSyncWrapp<T>(coll, Locker);
            else
                throw new ArgumentNullException("coll");
            return res;
        }
        #endregion

        #region ICollection<T> Members

        public void Add(T item)
        {
            using (Locker.Write(locker))
                inner.Add(item);
        }

        public void Clear()
        {
            using (Locker.Write(locker))
                inner.Clear();
        }

        public bool Contains(T item)
        {
            using (Locker.Read(locker))
                return inner.Contains(item);
        }

        public void CopyTo(T[] array, int arrayIndex)
        {
            using (Locker.Read(locker))
                inner.CopyTo(array, arrayIndex);
        }

        public int Count
        {
            get
            {
                using (Locker.Read(locker))
                    return inner.Count;
            }
        }

        public bool IsReadOnly
        {
            get
            {
                using (Locker.Read(locker))
                    return inner.IsReadOnly;
            }
        }

        public bool Remove(T item)
        {
            using (Locker.Write(locker))
                return inner.Remove(item);
        }

        #endregion

        #region IEnumerable<T> Members

        public IEnumerator<T> GetEnumerator()
        {
            return SynchronizedEnumerator<T>.Create(inner.GetEnumerator, locker);
        }

        #endregion

        #region IEnumerable Members

        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }

        #endregion
    }

    /// <summary>
    /// Уничтожаемый блокировщик ресурса по чтению-записи
    /// </summary>
    public class Locker : IDisposable
    {

        private ReaderWriterLockSlim rwls;
        private TypeLock typeLock;
        private Locker(ReaderWriterLockSlim rwls, TypeLock TypeLock)
        {
            this.rwls = rwls;
            this.typeLock = TypeLock;
            switch (typeLock)
            {
                case TypeLock.Read:
                    rwls.EnterReadLock();
                    break;
                case TypeLock.Write:
                    rwls.EnterWriteLock();
                    break;
                case TypeLock.UpgradeableRead:
                    rwls.EnterUpgradeableReadLock();
                    break;
            }
        }
        /// <summary>
        /// Вызов блокировки для чтения
        /// </summary>
        /// <param name="rwls">Объект синхронизации</param>
        /// <returns>Экземпляр блокировщика</returns>
        public static Locker Read(ReaderWriterLockSlim rwls)
        {
            return Create(rwls, TypeLock.Read);
        }
        /// <summary>
        /// Вызов блокировки для записи
        /// </summary>
        /// <param name="rwls">Объект синхронизации</param>
        /// <returns>Экземпляр блокировщика</returns>
        public static Locker Write(ReaderWriterLockSlim rwls)
        {
            return Create(rwls, TypeLock.Write);
        }
        /// <summary>
        /// Вызов блокировки для обновляемого чтения
        /// </summary>
        /// <param name="rwls">Объект синхронизации</param>
        /// <returns>Экземпляр блокировщика</returns>
        public static Locker UpgradeableRead(ReaderWriterLockSlim rwls)
        {
            return Create(rwls, TypeLock.UpgradeableRead);
        }
        private static Locker Create(ReaderWriterLockSlim rwls, TypeLock TypeLock)
        {
            if (rwls == null)
                throw new ArgumentNullException("rwls");
            return new Locker(rwls, TypeLock);
        }
        #region IDisposable Members

        public void Dispose()
        {
            switch (typeLock)
            {
                case TypeLock.Read:
                    rwls.ExitReadLock();
                    break;
                case TypeLock.Write:
                    rwls.ExitWriteLock();
                    break;
                case TypeLock.UpgradeableRead:
                    rwls.ExitUpgradeableReadLock();
                    break;
            }
        }

        #endregion
        private enum TypeLock
        {
            Read,
            Write,
            UpgradeableRead
        }
    }

    public delegate IEnumerator<T> dlgGetNumerator<T>();

    /// <summary>
    /// Потокобезопасный перечислитель 
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public class SynchronizedEnumerator<T> : IEnumerator<T>
    {
        IEnumerator<T> inner;
        ReaderWriterLockSlim lockObj;
        private SynchronizedEnumerator(dlgGetNumerator<T> inner, ReaderWriterLockSlim lockObj)
        {
            this.lockObj = (lockObj != null)
                ? lockObj : new ReaderWriterLockSlim();
            lockObj.EnterReadLock();
            this.inner = inner();
        }

        public static SynchronizedEnumerator<T> Create(dlgGetNumerator<T> inner, ReaderWriterLockSlim lockObj)
        {
            if (inner == null)
                throw new ArgumentNullException("inner");
            return new SynchronizedEnumerator<T>(inner, lockObj);
        }

        #region IEnumerator<T> Members

        public T Current
        {
            get { return inner.Current; }
        }

        #endregion

        #region IDisposable Members

        public void Dispose()
        {
            inner.Dispose();
            lockObj.ExitReadLock();
        }

        #endregion

        #region IEnumerator Members

        object IEnumerator.Current
        {
            get { return inner.Current; }
        }

        public bool MoveNext()
        {
            return inner.MoveNext();
        }

        public void Reset()
        {
            inner.Reset();
        }

        #endregion
    }

}
